<template>
    <div class="info-box" ref="myComponent">
        <div ref="graphContainer" class="graph-container"></div>
    </div>
</template>


<script setup>
    import * as d3 from 'd3';
    import { ref, shallowRef, onMounted, onBeforeUnmount, reactive } from 'vue';
    const graphData = reactive({ nodes: [], links: [] });
    const filteredData = reactive({ nodes: [], links: [] });
    const rootNodes = ref([]);
    const selectedRoot = ref('all');
    const simulation = ref(null);
    const svg = ref(null);
    const selectedNode = ref(null);
    const selectedRing = ref(null);
    const selectedLink = ref(null);
    const zoom = ref(null);
    const g = ref(null);

    // 表单相关状态
    const editorRef = shallowRef();
    const menuShow = ref(false);
    onMounted(() => {
        generateMockData();
        initGraph();
    });

    // 组件卸载前清理
    onBeforeUnmount(() => {
        if (editorRef.value) {
            editorRef.value.destroy();
        }
        if (simulation.value) {
            simulation.value.stop();
        }
    });

    // 生成模拟数据
    function generateMockData() {
        graphData.nodes = [];
        graphData.links = [];
        
        for (let i = 1; i <= 10; i++) {
            graphData.nodes.push({
                id: `root${i}`,
                name: `根节点${i}`,
                group: i,
                isRoot: true
            });
            
            const childCount = Math.floor(Math.random() * 6) + 5;
            for (let j = 1; j <= childCount; j++) {
                graphData.nodes.push({
                    id: `node${i}-${j}`,
                    name: `子节点${i}-${j}`,
                    group: i,
                    isRoot: false
                });
            }
        }

        graphData.nodes.forEach(node => {
            if (!node.isRoot) {
                const rootId = `root${node.group}`;
                graphData.links.push({
                    id: `link-${rootId}-${node.id}`,
                    source: rootId,
                    target: node.id,
                    value: Math.floor(Math.random() * 10) + 1,
                    relation: '包含'
                });

                if (Math.random() > 0.7) {
                    const siblings = graphData.nodes
                        .filter(n => n.group === node.group && n.id !== node.id && !n.isRoot);
                    if (siblings.length > 0) {
                        const randomSibling = siblings[Math.floor(Math.random() * siblings.length)];
                        graphData.links.push({
                            id: `link-${node.id}-${randomSibling.id}`,
                            source: node.id,
                            target: randomSibling.id,
                            value: Math.floor(Math.random() * 10) + 1,
                            relation: ['依赖', '关联', '引用'][Math.floor(Math.random() * 3)]
                        });
                    }
                }
            }
        });

        rootNodes.value = graphData.nodes.filter(node => node.isRoot);
    }

    // 初始化图表
    function initGraph() {
        const container = document.querySelector('.graph-container');
        const width = container.clientWidth;
        const height = container.clientHeight;

        if (svg.value) {
            svg.value.remove();
        }
        
        svg.value = d3.select(container)
            .append('svg')
            .attr('width', width)
            .attr('height', height);
        
        zoom.value = d3.zoom()
            .scaleExtent([0.1, 8])
            .on('zoom', (event) => {
                g.value.attr('transform', event.transform);
            });
        
        svg.value.call(zoom.value);
        g.value = svg.value.append('g');
        
        showAllGraph();
    }

    // 显示所有图表
    function showAllGraph() {
        filteredData.nodes = [...graphData.nodes];
        filteredData.links = [...graphData.links];
        createForceGraph();
    }

    // 创建力导向图
    function createForceGraph() {
        if (simulation.value) {
            simulation.value.stop();
        }

        g.value.selectAll('*').remove();

        // 创建力导向图
        simulation.value = d3.forceSimulation(filteredData.nodes)
            .force('link', d3.forceLink(filteredData.links).id(d => d.id).distance(100))
            .force('charge', d3.forceManyBody().strength(-300))
            .force('center', d3.forceCenter(svg.value.attr('width') / 2, svg.value.attr('height') / 2));

        // 创建箭头定义
        const defs = g.value.append('defs');
        
        defs.append('marker')
            .attr('id', 'arrowhead')
            .attr('viewBox', '0 -5 10 10')
            .attr('refX', 18)
            .attr('refY', 0)
            .attr('markerWidth', 6)
            .attr('markerHeight', 6)
            .attr('orient', 'auto')
            .append('path')
            .attr('d', 'M0,-5L10,0L0,5')
            .attr('fill', '#000');

        // 创建连线
        const link = g.value.append('g')
            .selectAll('line')
            .data(filteredData.links)
            .enter().append('line')
            .attr('class', 'link')
            .attr('stroke', '#999')
            .attr('stroke-width', 1)
            .attr('stroke-dasharray', '3,3')
            .attr('marker-end', 'url(#arrowhead)')
            .on('click', (event, d) => handleLinkClick(event, d));

        // 创建关系文字
        const linkText = g.value.append('g')
            .selectAll('text')
            .data(filteredData.links)
            .enter().append('text')
            .attr('class', 'link-text')
            .attr('font-size', 10)
            .attr('fill', '#000')
            .text(d => d.relation || d.target.name);

        // 创建节点
        const node = g.value.append('g')
            .selectAll('g.node')
            .data(filteredData.nodes)
            .enter().append('g')
            .attr('class', 'node')
            .style('cursor', 'pointer')
            .call(d3.drag()
                .on('start', dragstarted)
                .on('drag', dragged)
                .on('end', dragended));

        // 节点圆圈
        node.append('circle')
            .attr('r', d => {
                if (selectedRoot.value === 'all') {
                    return d.isRoot ? 25 : 20;
                } else {
                    return d.id === selectedRoot.value ? 30 : 20;
                }
            })
            .attr('fill', d => {
                const colors = ['#66c2a5', '#fc8d62', '#8da0cb','#66c2a5', '#fc8d62', '#8da0cb','#66c2a5', '#fc8d62', '#8da0cb', '#8da0cb'];
                return colors[d.group - 1];
            })
            .on('click', (event, d) => handleNodeClick(event, d));

        // 节点文字
        const nodeText = node.append('text')
            .attr('dy', 4)
            .attr('text-anchor', 'middle')
            .attr('fill', 'white')
            .style('font-size', d => {
                if (selectedRoot.value === 'all') {
                    return d.isRoot ? '10px' : '8px';
                } else {
                    return d.id === selectedRoot.value ? '10px' : '8px';
                }
            })
            .style('dominant-baseline', 'auto')
            .text(d => {
                const maxLength = 4;
                return d.name.length > maxLength ? 
                    d.name.substring(0, maxLength) + '...' : 
                    d.name;
            });
        
        // 应用文字
        nodeText.attr('clip-path', d => `url(#clip-${d.id})`);
        
        // 更新函数
        const ticked = () => {
            link
                .attr('x1', d => d.source.x)
                .attr('y1', d => d.source.y)
                .attr('x2', d => d.target.x - (d.target.x - d.source.x) * 15 / Math.sqrt(Math.pow(d.target.x - d.source.x, 2) + Math.pow(d.target.y - d.source.y, 2)))
                .attr('y2', d => d.target.y - (d.target.y - d.source.y) * 15 / Math.sqrt(Math.pow(d.target.x - d.source.x, 2) + Math.pow(d.target.y - d.source.y, 2)));

            linkText
                .attr('x', d => (d.source.x + d.target.x) / 2)
                .attr('y', d => (d.source.y + d.target.y) / 2);

            node.attr('transform', d => `translate(${d.x},${d.y})`);
            
            if (selectedRing.value && selectedNode.value) {
                selectedRing.value
                    .attr('cx', selectedNode.value.x)
                    .attr('cy', selectedNode.value.y);
            }
        };

        simulation.value.on('tick', ticked);
    }

    // 节点点击处理
    function handleNodeClick(event, d) {
        if (selectedRing.value) {
            selectedRing.value.remove();
        }

        const color = d3.select(event.currentTarget).attr('fill');
        const radius = d.isRoot ? 30 : 25; 

        selectedRing.value = g.value.append('circle')
            .attr('style', 'transform-box: fill-box; transform-origin: center; animation: rotate 6s linear infinite;')
            .attr('cx', d.x || 0) 
            .attr('cy', d.y || 0) 
            .attr('r', radius)
            .attr('fill', 'none')
            .attr('stroke', color)
            .attr('stroke-width', 2)
            .attr('stroke-dasharray', '5,5');
        
        selectedNode.value = d;
        menuShow.value = true
    }

    // 连线点击处理
    function handleLinkClick(event) {
        if (selectedLink.value) {
            selectedLink.value.attr('stroke-dasharray', '3,3');
            selectedLink.value.attr('stroke', '#999');
        }
        
        selectedLink.value = d3.select(event.currentTarget)
            .attr('stroke-dasharray', null)
            .attr('stroke', '#000000');
    }

    function dragstarted(event, d) {
        if (!event.active) simulation.value.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(event, d) {
        d.fx = event.x;
        d.fy = event.y;
    }

    function dragended(event, d) {
        if (!event.active) simulation.value.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    }
</script>

<style scoped>
    .info-box{background: #FFFFFF;width: 100%;position: relative;}
    .graph-container { width: 100%; min-height: 750px; overflow: hidden; }
    .link { cursor: pointer; }
    .selected-ring { transform-box: fill-box; transform-origin: center; animation: rotate 6s linear infinite; }
    @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } 
</style>